#ifndef __MQ_BINDING_HPP__
#define __MQ_BINDING_HPP__

#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <sstream>
#include "../mq_comm/mq_msg.pb.h"
#include "../mq_comm/helper.hpp"

namespace mq
{
    /*绑定信息类*/
    struct Binding
    {
        using ptr=std::shared_ptr<Binding>;
        std::string exchange_name;//交换机名称
        std::string msgqueue_name;//队列名称
        std::string bingding_key;//绑定信息密钥

        Binding()=default;
        Binding(const std::string& _exchange_name,const std::string& _queue_name,const std::string& _binding_key)
            :exchange_name(_exchange_name),msgqueue_name(_queue_name),bingding_key(_binding_key)
        {}
    };

    //队列与绑定信息是一对一的关系,而交换机与队列是一对多的关系
    //建立队列名称和绑定信息之间的映射关系
    using MsgQueueBindingMap=std::unordered_map<std::string,Binding::ptr>;
    //建议交换机名称与MsgQueueBindingMap集合之间的映射关系
    using BindingMap=std::unordered_map<std::string,MsgQueueBindingMap>;
    /*操作绑定信息数据库类*/
    class BindingMapper
    {
    public:
        BindingMapper(const std::string& filename)
            :_sql_helper(filename)
        {
            //考虑路径文件的父目录可能还不存在,所以优先创建父目录
            std::string parentPath=FileHelper::parentDirectory(filename);
            FileHelper::createDirectory(parentPath);
            assert(_sql_helper.open());
            createTable();
        }

        /*创建数据库表*/
        void createTable()
        {
            #define CREATE_BINDING "create table if not exists binding_table(\
            exchange_name varchar(32),\
            msgqueue_name varchar(32),\
            binding_key varchar(128));"

            assert(_sql_helper.exec(CREATE_BINDING,nullptr,nullptr));
        }

        /*删除数据库表*/
        void removeTable()
        {
            #define DROP_BINDING "drop table if exists binding_table;"
            _sql_helper.exec(DROP_BINDING,nullptr,nullptr);
        }

        /*插入绑定信息*/
        bool insert(Binding::ptr& bingding)
        {
            std::stringstream ss;
            ss<<"insert into binding_table values(";
            ss<<"'"<<bingding->exchange_name<<"', ";
            ss<<"'"<<bingding->msgqueue_name<<"', ";
            ss<<"'"<<bingding->bingding_key<<"');";
            return _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        /*根据交换机名称与队列名称删除指定绑定信息内容*/
        void remove(const std::string& ename,const std::string& qname)
        {
            std::stringstream ss;
            ss<<"delete from binding_table where exchange_name=";
            ss<<"'"<<ename<<"' and msgqueue_name=";
            ss<<"'"<<qname<<"';";
            _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        /*根据交换机名称删除指定绑定信息内容*/
        void removeExchangeBinding(const std::string& ename)
        {
            std::stringstream ss;
            ss<<"delete from binding_table where exchange_name=";
            ss<<"'"<<ename<<"';";
            _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        /*根据队列名称删除指定绑定信息内容*/
        void removeMsgQueueBinding(const std::string& qname)
        {
            std::stringstream ss;
            ss<<"delete from binding_table where msgqueue_name=";
            ss<<"'"<<qname<<"';";
            _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        /*查找所有绑定信息内容,并恢复信息*/
        BindingMap recovery()
        {
            BindingMap result;
            const std::string& query="select exchange_name,msgqueue_name,binding_key from binding_table;";
            _sql_helper.exec(query,selectCallback,&result);
            return result;
        }
    
    private:
        static int selectCallback(void* args, int col, char** rows, char** fields)
        {
            BindingMap* result=(BindingMap*)args;
            Binding::ptr bp=std::make_shared<Binding>(rows[0],rows[1],rows[2]);
            MsgQueueBindingMap& qmap=(*result)[bp->exchange_name];//以引用去弄，不存在就会创建
            qmap.insert(std::make_pair(bp->msgqueue_name,bp));
            return 0;
        }
    
    private:
        SqliteHelper _sql_helper;
    };

    /*绑定信息管理类*/
    class BindingManager
    {
    public:
        using ptr=std::shared_ptr<BindingManager>;
        BindingManager(const std::string& filename)
            :_mapper(filename)
        {
            _bindings=_mapper.recovery();
        }

        /*绑定新的绑定信息*/
        bool bind(const std::string& ename,const std::string& qname,const std::string& key,bool durable)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eiter=_bindings.find(ename);
            if(eiter!=_bindings.end()&&eiter->second.find(qname)!=eiter->second.end()) return true;

            Binding::ptr pb=std::make_shared<Binding>(ename,qname,key);
            if(durable==true)
            {
                bool ret=_mapper.insert(pb);
                if(ret==false) return false;
            }
            auto& qmap=_bindings[ename];//使用引用不存在就会创建
            qmap.insert(std::make_pair(qname,pb));
            return true;
        }

        /*根据交换机名称和队列名称删除绑定信息内容*/
        void unBind(const std::string& ename,const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eiter=_bindings.find(ename);
            if(eiter==_bindings.end()) return;
            auto qiter=eiter->second.find(qname);
            if(qiter==eiter->second.end()) return;
            _mapper.remove(ename,qname);
            _bindings[ename].erase(qname);
        }

        /*根据交换机名称删除绑定信息内容*/
        void removeExchangeBindings(const std::string& ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto iter=_bindings.find(ename);
            if(iter==_bindings.end()) return;
            _mapper.removeExchangeBinding(ename);
            _bindings.erase(ename);
        }

        /*根据队列名称删除绑定信息内容*/
        void removeMsgQueueBindings(const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeMsgQueueBinding(qname);
            for(auto& iter:_bindings)
            {
                iter.second.erase(qname);
            }
        }

        /*根据交换机名称获取与指定交换机相关的所有绑定信息*/
        MsgQueueBindingMap getExchangeBindings(const std::string& ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto iter=_bindings.find(ename);
            if(iter==_bindings.end()) MsgQueueBindingMap();
            return iter->second;
        }

        /*根据交换机名称和队列名称获取相关绑定信息*/
        Binding::ptr getBinding(const std::string& ename,const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eiter=_bindings.find(ename);
            if(eiter==_bindings.end()) return Binding::ptr();
            auto qiter=eiter->second.find(qname);
            if(qiter==eiter->second.end()) return Binding::ptr();
            return qiter->second;
        }

        /*根据交换机名称和队列名称判断相关绑定信息是否存在*/
        bool exists(const std::string& ename,const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eiter=_bindings.find(ename);
            if(eiter==_bindings.end()) return false;
            auto qiter=eiter->second.find(qname);
            if(qiter==eiter->second.end()) return false;
            return true;
        }

        /*获取所有绑定信息数量*/
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            size_t total_size=0;
            for(auto& iter:_bindings)
            {
                total_size+=iter.second.size();
            }
            return total_size;
        }

        /*清理所有绑定信息*/
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _bindings.clear();
        }
    private:
        std::mutex _mutex;//互斥锁
        BindingMapper _mapper;//操作绑定数据库表对象
        BindingMap _bindings;//内存存储所有绑定信息内容
    };
}

#endif